1 module hip.gui.button;
2 import hip.api.graphics.g2d.renderer2d;
3 public import hip.gui.widget;
4 
5 
6 interface IButtonRenderer 
7 {
8     void render(Widget.Bounds worldBounds, Button.State state);
9     static IButtonRenderer DebugButtonRenderer()
10     {
11         __gshared IButtonRenderer dbgRenderer;
12         if(dbgRenderer is null)
13         {
14             dbgRenderer = new class IButtonRenderer
15             {
16                 void render(Widget.Bounds worldBounds, Button.State state) const
17                 {
18                     alias b = worldBounds;
19                     final switch(state) with(Button.State)
20                     {
21                         case idle: fillRoundRect(b.x, b.y, b.width, b.height, 12, HipColor.red);
22                             break;
23                         case hovered: fillRoundRect(b.x, b.y, b.width, b.height, 12, HipColor(255, 127, 0));
24                             break;
25                         case pressed: fillRoundRect(b.x, b.y, b.width, b.height, 12, HipColor(127, 0, 0));
26                             break;
27                     }
28                 }
29             };
30         }
31         return dbgRenderer;
32     }
33 }
34 
35 class SpriteButtonRenderer : IButtonRenderer
36 {
37     IHipTextureRegion idle;
38     IHipTextureRegion hover;
39     IHipTextureRegion pressed;
40 
41 
42     void render(Widget.Bounds worldBounds, Button.State state)
43     {
44         IHipTextureRegion target;
45         final switch ( state )
46         {
47             case Button.state.idle: target = idle; break;
48             case Button.state.hovered: target = hover; break;
49             case Button.state.pressed: target = pressed; break;
50         }
51         drawRegion(target, worldBounds.x, worldBounds.y);
52     }
53 }
54 
55 
56 /**
57  * Button does not propagates findWidgetAt after it.
58  * This happens mostly because when using buttons, it is common to not find its childrens after it since it handles most pointer events.
59  * If that is not desired, simply change `propagates` to true.
60  */
61 class Button : Widget
62 {
63     enum State
64     {
65         idle,
66         pressed,
67         hovered,
68     }
69 
70     State state;
71     IButtonRenderer renderer;
72 
73     protected void delegate() onClick;
74     protected void delegate() onHover;
75 
76     this(int x, int y, int w, int h)
77     {
78         setPosition(x,y);
79         width = w;
80         height = h;
81         renderer = IButtonRenderer.DebugButtonRenderer;
82         propagates = false;
83     }
84     Button setOnHover(void delegate() onHover)
85     {
86         this.onHover = onHover;
87         return this;
88     }
89     Button setOnClick(void delegate() onClick)
90     {
91         this.onClick = onClick;
92         return this;
93     }
94 
95     override void onMouseEnter()
96     {
97         if(onHover) onHover();
98         state = State.hovered;
99     }
100     override void onMouseDown()
101     {
102         state = State.pressed;
103     }
104     override void onMouseUp()
105     {
106         if(state == State.pressed && onClick) onClick();
107         state = State.idle;
108     }
109     override void onMouseExit()
110     {
111         if(state != State.pressed)
112             state = State.idle;
113 
114     }
115 
116     void setButtonRenderer(IButtonRenderer renderer)
117     {
118         this.renderer = renderer;
119     }
120     override void onRender()
121     {
122         renderer.render(Bounds(worldTransform.x, worldTransform.y, width, height), state);
123     }
124 }